package com.reyco1.platformengine.character
{
	import com.reyco1.platformengine.core.CoordinateUtil;
	import com.reyco1.platformengine.core.GameEngine;
	import com.reyco1.platformengine.map.IMap;
	import com.reyco1.platformengine.tile.BaseTile;
	import com.reyco1.platformengine.tile.MovingTile;
	import com.reyco1.platformengine.vo.Coordinates;
	import com.reyco1.platformengine.vo.CornersData;
	
	import flash.display.Graphics;
	import flash.display.Sprite;

	public class BaseCharacter extends Sprite
	{
		private var base:Sprite;
		private var originalSpeed:Number;
		private var originalSpeedSet:Boolean;
		private var lastPosY:Number;
		
		public var currentMovingTile:MovingTile;
		public var yIncrement:Number;

		protected var _charWidth:Number;
		protected var _charHeight:Number;
		protected var _defColor:Number;
		protected var _currentCoordinates:Coordinates;
		protected var _cornerTiles:CornersData;
		protected var _speed:Number;
		protected var _jumpSpeed:Number;
		protected var _jumpStart:Number;
		protected var _gravity:Number;
		protected var _controllable:Boolean;
		protected var _isJumping:Boolean;
		protected var _isClimbing:Boolean;
		protected var _isOnSlope:Boolean;
		protected var _engine:GameEngine;
		protected var _map:IMap;
		
		public var movingTiles:Array = [];
		
		public function BaseCharacter(w:Number, h:Number)
		{
			currentMovingTile = null;
			originalSpeedSet  = false;
			base 			  = null;
			charWidth 		  = w;
			charHeight 		  = h;
			defColor 		  = 0xFF0000;
			controllable 	  = false;
			isJumping 		  = false;
			isClimbing 		  = false;
			
			build();
		}
		
		public function build():void
		{
			if(base != null)
			{
				while(numChildren > 0) removeChildAt(0);
				base = null;
			}
			
			base = new Sprite();
			
			var g:Graphics = base.graphics;
			g.beginFill(defColor);
			g.drawRect(0, 0, charWidth, charHeight);
			g.endFill();
			
			base.x = (charWidth  * 0.5) * -1;
			base.y = (charHeight * 0.5) * -1;
			
			addChild(base);
		}
		
		public function move(xFactor:Number, yFactor:Number, jumpFactor:Number = 0):void
		{
			lastPosY = y;
			
			var newSpeed:Number;
			if(Math.abs(jumpFactor) == 1)
			{
				newSpeed = jumpSpeed * jumpFactor;
			}
			else
			{
				newSpeed = originalSpeed;
			}
			speed = newSpeed;						
			
			cornerTiles = CoordinateUtil.getCorners(this, map, engine, x, y + speed * yFactor); 
			
			if(yFactor == -1)
			{
				if(cornerTiles.upleft && cornerTiles.upright)
				{
					y += speed * yFactor;
				}
				else
				{
					y = currentCoordinates.yTile * map.tileH + (height * 0.5);
					jumpSpeed = 0;
				}
			}
			
			if(yFactor == 1)
			{
				if(cornerTiles.downleft && cornerTiles.downright && !checkIfOnCloud() && !checkForMovingTile(speed * yFactor))
				{
					y += speed * yFactor;
				}
				else
				{
					isJumping = false;
					if(currentMovingTile != null)
					{
						y = currentMovingTile.y - (currentMovingTile.height * 0.5) - (height * 0.5);
					}
					else
					{
						y = (currentCoordinates.yTile + 1) * map.tileH - (height * 0.5);
					}
				}
			}
			
			cornerTiles = CoordinateUtil.getCorners(this, map, engine, x + speed * xFactor, y);
			
			if(xFactor == -1)
			{
				if((cornerTiles.downleft && cornerTiles.upleft) || isOnSlope)
				{
					x += speed * xFactor;
					fall();
				}
				else
				{
					x = currentCoordinates.xTile * map.tileW + (width * 0.5);
				}
			}
			
			if(xFactor == 1)
			{
				if((cornerTiles.upright && cornerTiles.downright) || isOnSlope)
				{
					x += speed * xFactor;
					fall();
				}
				else
				{
					x = (currentCoordinates.xTile + 1) * map.tileW - (width * 0.5);
				}
			}
			
			update();
		}
		
		public function fall():void
		{
			if(!isJumping)
			{
				cornerTiles = CoordinateUtil.getCorners(this, map, engine, x, y + 1);
				if(cornerTiles.downleft && cornerTiles.downright && !checkIfOnCloud())
				{
					jumpSpeed = 0;
					isJumping = true;
				}
			}			
			isClimbing = false;
		}
		
		public function jump():void
		{
			currentMovingTile = null
			
			jumpSpeed = jumpSpeed + gravity;
			
			if(jumpSpeed > map.tileH - (height * 0.5))
			{
				jumpSpeed = map.tileH - (height * 0.5);
			}
			
			if(jumpSpeed < 0)
			{
				move(0, -1, -1)
			}
			else if(jumpSpeed > 0)
			{
				move(0, 1, 1)
			}
		}
		
		public function climb(yFactor:Number):void
		{
			isClimbing = true;
			isJumping = false;
			y += speed * yFactor;
			x = (currentCoordinates.xTile * map.tileW) + (map.tileW * 0.5);
		}
		
		public function update():void
		{
			if(currentMovingTile != null)
			{
				cornerTiles = CoordinateUtil.getCorners(this, map, engine, x, y + currentMovingTile.speed * currentMovingTile.dirY);
				if(currentMovingTile.dirY == -1)
				{
					if(cornerTiles.upleft && cornerTiles.upright)
					{
						y = currentMovingTile.y - (currentMovingTile.height * 0.5) - (height * 0.5);
					}
					else
					{
						y = currentCoordinates.yTile * map.tileH + (height * 0.5);
				        jumpSpeed = 0;
				        isJumping = true;
				        currentMovingTile = null;				        
					}
				}
				if(currentMovingTile.dirY == 1)
				{
					if(cornerTiles.downleft && cornerTiles.downright)
					{
						y = currentMovingTile.y - (currentMovingTile.height * 0.5) - (height * 0.5);
					}
					else
					{
						currentMovingTile = null;
        				y = (currentCoordinates.yTile + 1) * map.tileH - (height * 0.5);
					}
				}
				
				cornerTiles = CoordinateUtil.getCorners(this, map, engine, x + currentMovingTile.speed * currentMovingTile.dirX, y);
				if(currentMovingTile.dirX == -1)
				{
					if(cornerTiles.downleft && cornerTiles.upleft)
					{
						x += (currentMovingTile.speed * 0.5) * currentMovingTile.dirX;
					}
					else
					{
						x = (currentCoordinates.xTile * 0.5) * map.tileW + (width * 0.5);
        				fall();
					}
			    }
			    if(currentMovingTile.dirX == 1)
				{
			    	if(cornerTiles.upright && cornerTiles.downright)
					{
			    		x += (currentMovingTile.speed * 0.5) * currentMovingTile.dirX;
			    	}
					else
					{
			    		fall();
        				x = ((currentCoordinates.xTile + 1)* 0.5) * map.tileW - (width * 0.5);
			    	}
			    }
      			
			}
			
			currentCoordinates.xTile = Math.floor(x / map.tileW);
			currentCoordinates.yTile = Math.floor(y / map.tileH);
			
			checkForSlopes(currentCoordinates.yTile, currentCoordinates.xTile);
		}
		
		private function checkIfOnCloud():Boolean
		{
			var bol:Boolean;
			
			var leftcloud:Boolean  = CoordinateUtil.getTile(engine, cornerTiles.downY, cornerTiles.leftX).isCloud;
			var rightcloud:Boolean = CoordinateUtil.getTile(engine, cornerTiles.downY, cornerTiles.rightX).isCloud;
			
			if(leftcloud || rightcloud)
			{
				bol = true;
			}
			else
			{
				bol = false;
			}			
			
			return bol;
		}
		
		public function checkForUpLadder():Boolean
		{
			var bol:Boolean;
			var downY:Number 		= Math.floor((y + (height * 0.5) - 1) / map.tileH);
			var upY:Number 			= Math.floor(((y - (height * 0.5)) - speed) / map.tileW);
			var upLadder:Boolean 	= CoordinateUtil.getTile(engine, upY, currentCoordinates.xTile).isLadder;
			var downLadder:Boolean 	= CoordinateUtil.getTile(engine, downY, currentCoordinates.xTile).isLadder;
			var upBlock:Boolean 	= CoordinateUtil.getTile(engine, upY, currentCoordinates.xTile).walkable;
			
			if(upLadder || upBlock)
			{
				if(upLadder || downLadder)
				{
					bol = true;
				}
			}
			else
			{
				y = currentCoordinates.yTile * map.tileH + (height * 0.5);
    			update();
    			bol = false
			}
			
			return bol;
		}
		
		public function checkForDownLadder():Boolean
		{
			var bol:Boolean;
			var downY:Number 	   = Math.floor((speed + y + (height * 0.5)) / map.tileH);
			var downLadder:Boolean = CoordinateUtil.getTile(engine, downY, currentCoordinates.xTile).isLadder;
			
			if(downLadder)
			{
				bol = true;
			}
			else
			{
				fall();
				bol = false
			}
			
			return bol;
		}
		
		public function checkForMovingTile(yPos:Number, directionY:Number = 1):Boolean
		{
			var bol:Boolean 	= false;
			currentMovingTile 	= null;
			
			if(directionY != -1)
			{
				var heroMaxY:Number = y + (height * 0.5) + yPos;
			    var heroMaxX:Number = x + (width * 0.5);
			    var heroMinX:Number = x - (width * 0.5);
			    
			    for(var a:Number = 0; a<movingTiles.length; a++)
				{
			    	var movingTile:MovingTile = movingTiles[a] as MovingTile;
			    	var tileMaxY:Number = movingTile.y + (movingTile.height * 0.5);
				    var tileMinY:Number = movingTile.y - (movingTile.height * 0.5);
				    var tileMaxX:Number = movingTile.x + (movingTile.width * 0.5);
				    var tileMinX:Number = movingTile.x - (movingTile.width * 0.5);
					
				    if((lastPosY + (height * 0.5)) <= tileMinY)
					{
				    	if(heroMaxY <= tileMaxY && heroMaxY >= tileMinY)
						{
				    		if(heroMaxX > tileMinX && heroMaxX < tileMaxX)
							{
				    			currentMovingTile = movingTile;
					            bol = true;
					            break;
				    		}
							else if(heroMinX > tileMinX && heroMinX < tileMaxX)
							{
				    			currentMovingTile = movingTile;
					            bol = true;
					            break;
				    		}
				    	}
				    }
			    }
			}			
			return bol;
		}
		
		public function checkForSlopes(xFactor:Number, yFactor:Number):void
		{
			if(CoordinateUtil.getTile(engine, currentCoordinates.yTile + 1, currentCoordinates.xTile).isSlope && !isJumping)
			{
				currentCoordinates.yTile += 1;
				y += map.tileH;
			}
			
			if(CoordinateUtil.getTile(engine, currentCoordinates.yTile, currentCoordinates.xTile).isSlope && yFactor != -1)
			{
				if(yFactor == 1)
				{
					y = (currentCoordinates.yTile + 1) * map.tileH - (height * 0.5);
				}
				
				var xPos:Number = x - currentCoordinates.xTile * map.tileW;
				
				isOnSlope = CoordinateUtil.getTile(engine, currentCoordinates.yTile, currentCoordinates.xTile).isSlope;
				isJumping = false;
				
				if(CoordinateUtil.getTile(engine, currentCoordinates.yTile, currentCoordinates.xTile).slopeFactor == 1)
				{
					yIncrement = xPos;
					y = (currentCoordinates.yTile + 1) * map.tileH - (height * 0.5) - yIncrement;
				}
				else
				{
					yIncrement = map.tileW - xPos;
      				y = (currentCoordinates.yTile + 1) * map.tileH - (height * 0.5) - yIncrement;
				}
			}
			else
			{
				if((isOnSlope && xFactor == 1) || (!isOnSlope && xFactor == -1))
				{
					currentCoordinates.yTile -= 1;
				    y -= map.tileH;
				}
			}
			isOnSlope = false;
		}
		
		/*
		properties
		*/
		
		public function get charWidth():Number
		{
			return _charWidth;
		}

		public function set charWidth(value:Number):void
		{
			_charWidth = value;
		}

		public function get charHeight():Number
		{
			return _charHeight;
		}

		public function set charHeight(value:Number):void
		{
			_charHeight = value;
		}

		public function get defColor():Number
		{
			return _defColor;
		}

		public function set defColor(value:Number):void
		{
			_defColor = value;
		}

		public function get currentCoordinates():Coordinates
		{
			return _currentCoordinates;
		}

		public function set currentCoordinates(value:Coordinates):void
		{
			_currentCoordinates = value;
		}

		public function get cornerTiles():CornersData
		{
			return _cornerTiles;
		}

		public function set cornerTiles(value:CornersData):void
		{
			_cornerTiles = value;
		}

		public function get jumpSpeed():Number
		{
			return _jumpSpeed;
		}

		public function set jumpSpeed(value:Number):void
		{
			_jumpSpeed = value;
		}

		public function get jumpStart():Number
		{
			return _jumpStart;
		}

		public function set jumpStart(value:Number):void
		{
			_jumpStart = value;
		}

		public function get gravity():Number
		{
			return _gravity;
		}

		public function set gravity(value:Number):void
		{
			_gravity = value;
		}

		public function get controllable():Boolean
		{
			return _controllable;
		}

		public function set controllable(value:Boolean):void
		{
			_controllable = value;
		}

		public function get isJumping():Boolean
		{
			return _isJumping;
		}

		public function set isJumping(value:Boolean):void
		{
			_isJumping = value;
		}

		public function get isClimbing():Boolean
		{
			return _isClimbing;
		}

		public function set isClimbing(value:Boolean):void
		{
			_isClimbing = value;
		}
		
		public function get engine():GameEngine
		{
			return _engine;
		}

		public function set engine(value:GameEngine):void
		{
			_engine = value;
		}

		public function get map():IMap
		{
			return _map;
		}

		public function set map(value:IMap):void
		{
			_map = value;
		}

		public function get speed():Number
		{
			return _speed;
		}
		
		public function get isOnSlope():Boolean
		{
			return _isOnSlope;
		}

		public function set isOnSlope(value:Boolean):void
		{
			_isOnSlope = value;
		}

		public function set speed(value:Number):void
		{
			_speed = value;
			if(!originalSpeedSet)
			{
				originalSpeedSet = true;
				originalSpeed 	 = _speed;
			}
		}
	}
}